/**
 * @see https://github.com/qwikifiers/qwik-ui/blob/main/packages/kit-headless/src/components/tooltip/tooltip.tsx
 * @see https://floating-ui.com/docs/tutorial#functionality
 */
import { $, component$, Slot, useId, useSignal, useVisibleTask$ } from '@builder.io/qwik';
import { offset as _offset, flip, shift, arrow, computePosition, type ComputePositionConfig } from '@floating-ui/dom';
import { cn } from '../cn';

export interface TooltipProps {
  class?: string;
  content: string;
  inline?: boolean;
  position?: ComputePositionConfig['placement'];
}

type _number = number | undefined;

const placementToSide: Record<string, 'top' | 'bottom' | 'left' | 'right'> = {
  top: 'top',
  'top-end': 'top',
  'top-start': 'top',
  bottom: 'bottom',
  'bottom-end': 'bottom',
  'bottom-start': 'bottom',
  left: 'left',
  'left-end': 'left',
  'left-start': 'left',
  right: 'right',
  'right-end': 'right',
  'right-start': 'right',
};

type State = 'hidden' | 'unpositioned' | 'positioned';

export default component$(({ content, position = 'top', ...props }: TooltipProps) => {
  const id = useId();

  const triggerAnchor = useSignal<HTMLElement>();
  const tooltipAnchor = useSignal<HTMLElement>();
  const arrowAnchor = useSignal<HTMLElement>();

  const stateSignal = useSignal<State>('hidden');

  const positionSignal = useSignal<{ x: number; y: number }>({ x: 0, y: 0 });
  const arrowPosSignal = useSignal<{ top?: _number; bottom?: _number; left?: _number; right?: _number }>({});
  const Wrapper: keyof HTMLElementTagNameMap = props.inline ? 'span' : 'div';

  const update = $(async () => {
    if (triggerAnchor.value && tooltipAnchor.value && arrowAnchor.value) {
      const { x, y, middlewareData } = await computePosition(triggerAnchor.value, tooltipAnchor.value as HTMLElement, {
        placement: position,
        middleware: [_offset(6), flip(), shift({ padding: 4 }), arrow({ element: arrowAnchor.value })],
      });

      positionSignal.value = { x, y };

      // Accessing the data
      const arrowPos = middlewareData.arrow;
      const side = placementToSide[position];
      arrowPosSignal.value = {
        left: side == 'right' ? -4 : arrowPos?.x,
        top: side == 'bottom' ? -4 : arrowPos?.y,
        right: side == 'left' ? -4 : arrowPos?.x,
        bottom: side == 'top' ? -4 : arrowPos?.y,
      };
      stateSignal.value = 'positioned';
    }
  });

  const showTooltip = $(() => {
    stateSignal.value = 'unpositioned';
  });

  const hideTooltip = $(() => {
    stateSignal.value = 'hidden';
  });

  // eslint-disable-next-line qwik/no-use-visible-task
  useVisibleTask$(({ track }) => {
    const theState = track(() => stateSignal.value);
    if (theState === 'unpositioned') {
      update();
    }
  });

  return (
    <>
      <Wrapper
        style="width: fit-content;"
        tabIndex={0}
        ref={triggerAnchor}
        onMouseEnter$={() => showTooltip()}
        onMouseLeave$={() => hideTooltip()}
        onFocus$={() => showTooltip()}
        onBlur$={() => hideTooltip()}
        // need an id for the tooltip;
        aria-describedby={`#${id}`}
      >
        <Slot />
      </Wrapper>
      <div
        class={cn(
          'bg-kq-900 text-kq-50 p-2 text-sm rounded w-max absolute',
          stateSignal.value == 'hidden' ? 'hidden' : stateSignal.value == 'unpositioned' ? 'invisible' : '',
          props.class
        )}
        id={id}
        style={{ left: positionSignal.value.x, top: positionSignal.value.y }}
        ref={tooltipAnchor}
        role="tooltip"
        {...props}
      >
        {content}
        {/* {content ? content : <Slot name="tooltip-content" />} */}
        <div
          ref={arrowAnchor}
          class="absolute bg-kq-900 w-2 h-2 rotate-45"
          style={{
            left: arrowPosSignal.value.left,
            top: arrowPosSignal.value.top,
            right: arrowPosSignal.value.right,
            bottom: arrowPosSignal.value.bottom,
          }}
        />
      </div>
    </>
  );
});
